package jinyilw.swing;

import jinyilw.common.OSTools;
import jinyilw.swing.graphics.GraphicsTools;
import jinyilw.swing.i18n.ICSetting;
import jinyilw.swing.rlaf.utils.RBorderFactory;
import jinyilw.swing.visuals.Icomp.IRichTextLabel;
import jinyilw.swing.visuals.layout.TitleLayout;

import javax.imageio.ImageIO;
import javax.swing.Action;
import javax.swing.BorderFactory;
import javax.swing.ButtonGroup;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JInternalFrame;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JRadioButton;
import javax.swing.JScrollPane;
import javax.swing.JSpinner;
import javax.swing.JSpinner.DefaultEditor;
import javax.swing.JToolTip;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.ToolTipManager;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.text.JTextComponent;
import java.awt.Color;
import java.awt.Component;
import java.awt.ComponentOrientation;
import java.awt.Container;
import java.awt.Cursor;
import java.awt.Dialog;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Frame;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.LayoutManager;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.event.ActionListener;
import java.awt.event.AdjustmentListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Area;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;

public class SwingTools
{
	public static final String chSpaceStr = "&#12288;";
	public static final String enSpaceStr = "&nbsp;";
	private static final String ELLIPSIS = "...";

	private SwingTools()
	{
	}

	public static JFrame getMainFrame()
	{
		for (Window w : Window.getWindows())
			if (w instanceof JFrame && w.isVisible())
				return (JFrame) w;
		return null;
	}

	public static Window getWindowForComponent(Component com)
	{
		if (com == null)
			return JOptionPane.getRootFrame();
		if (com instanceof Frame || com instanceof Dialog)
			return (Window) com;
		return getWindowForComponent(com.getParent());
	}

	public static Window getWindow(Component c)
	{
		for (Component p = c; p != null; p = p.getParent())
			if (p instanceof Window)
				return (Window) p;
		return null;
	}

	public static String getWrapHtmlText(String longText, JComponent com,
			int width)
	{
		return getWrapHtmlText(longText, com, width, true);
	}

	public static String getWrapHtmlText(String longText, JComponent com,
			int width, boolean spaceable)
	{
		if (longText == null || com == null || width < 3)
			return longText;
		return getChWrapHtmlText(longText, com.getFontMetrics(com.getFont()),
				width, spaceable);
	}

	public static ArrayList<String> getChWrapTexts(String longText,
			JComponent com, int width)
	{
		if (com == null || com.getFont() == null)
			return new ArrayList<>();
		return getChWrapTexts(longText, com.getFontMetrics(com.getFont()),
				width);
	}

	public static Point getRelLocation(Component c)
	{
		if (c == null || !c.isShowing())
		{
			return new Point(0, 0);
		}

		Container parent = getRootContainer(c);
		if ((parent != null) && parent.isShowing() && c.isShowing())
		{
			Point p1 = c.getLocationOnScreen();
			Point p2 = parent.getLocationOnScreen();
			return new Point(p1.x - p2.x, p1.y - p2.y);
		}

		return new Point(0, 0);
	}

	public static Container getRootContainer(Component c)
	{
		if (c == null)
			return null;
		Container parent = c.getParent();
		while ((parent != null) && !(parent instanceof JPopupMenu)
				&& !(parent instanceof JInternalFrame)
				&& !(parent instanceof Window) && (parent.getParent() != null))
		{
			parent = parent.getParent();
		}
		return parent;
	}

	public static JPopupMenu createPopMenu(String[] strings,
			ActionListener... listeners)
	{
		return createPopMenu(strings, FontTools.defaultFont, listeners);
	}

	public static JPopupMenu createPopMenu(String[] strings, Font font,
			ActionListener... listeners)
	{
		if (strings == null)
			return null;

		boolean singleL = listeners.length == 1;
		boolean multiL = listeners.length >= strings.length;
		JPopupMenu popup = new JPopupMenu();
		JMenuItem item;
		for (int i = 0; i < strings.length; i++)
		{
			item = new JMenuItem(strings[i]);
			if (multiL)
				item.addActionListener(listeners[i]);
			else if (singleL)
				item.addActionListener(listeners[0]);
			if (font != null)
				item.setFont(font);
			popup.add(item);
		}

		return popup;
	}

	public static JScrollPane createTransScrollPane(Component view)
	{
		JScrollPane scrollPane = new JScrollPane(view);
		scrollPane.setOpaque(false);
		scrollPane.getViewport().setOpaque(false);
		return scrollPane;
	}

	public static JPanel createTransBorderPane()
	{
		JPanel panel = new JPanel();
		panel.setOpaque(false);
		panel.setBorder(RBorderFactory.getDefaultBorder());
		return panel;
	}

	public static void addTitleComponents(Container parent,
			Component... components)
	{
		if (parent == null || components == null)
			return;
		for (Component c : components)
			if (c != null)
				parent.add(c);
		LayoutManager layout = parent.getLayout();
		if (layout instanceof TitleLayout)
		{
			((TitleLayout) layout).addNewComponent(components);
		} else
		{
			TitleLayout titleLayout = new TitleLayout(layout, components);
			parent.setLayout(titleLayout);
		}
	}

	public static void selectNoneRadioButtons(JRadioButton... radioButtons)
	{
		if (radioButtons != null)
			for (JRadioButton radioButton : radioButtons)
				if (radioButton != null)
				{
					ButtonGroup group = radioButton
							.getModel().getGroup();
					group.remove(radioButton);
					radioButton.setSelected(false);
					group.add(radioButton);
				}
	}

	public static void makeSpinnerTextCenter(JSpinner spinner)
	{
		JComponent editor = spinner.getEditor();
		if (editor instanceof DefaultEditor)
			((DefaultEditor) editor).getTextField()
					.setHorizontalAlignment(SwingConstants.CENTER);
	}

	public static JMenuItem createMenuItem(JMenu menu, String text,
			String mnemonic, String description, Action action)
	{
		JMenuItem mi = menu.add(new JMenuItem(text));
		mi.setMnemonic(mnemonic.charAt(0));
		mi.getAccessibleContext().setAccessibleDescription(description);
		mi.addActionListener(action);
		if (action == null)
			mi.setEnabled(false);
		return mi;
	}

	/**
	 * Updates a Swing resource bundle in use from the current Locale.
	 */
	static void updateSwingResourceBundle(String swingResource,
			List<ClassLoader> classLoaders, String language)
	{
		ResourceBundle resource;
		try
		{
			Locale defaultLocale = language == null
					? Locale.getDefault()
					: (language.indexOf('_') == -1
					? new Locale(language)
					: new Locale(language.substring(0, 2),
					language.substring(3, 5)));
			resource = ResourceBundle.getBundle(swingResource, defaultLocale);
			for (ClassLoader classLoader : classLoaders)
			{
				ResourceBundle bundle = ResourceBundle.getBundle(swingResource,
						defaultLocale, classLoader);
				if (defaultLocale.equals(bundle.getLocale()))
				{
					resource = bundle;
					break;
				} else if (!resource.getLocale().getLanguage()
						.equals(bundle.getLocale().getLanguage())
						&& defaultLocale.getLanguage()
						.equals(bundle.getLocale().getLanguage()))
				{
					resource = bundle;
				}
			}
		} catch (MissingResourceException ex)
		{
			resource = ResourceBundle.getBundle(swingResource, Locale.ENGLISH);
		}

		// Update UIManager properties
		final String textAndMnemonicSuffix = ".textAndMnemonic";
		for (Enumeration<?> it = resource.getKeys(); it.hasMoreElements(); )
		{
			String key = (String) it.nextElement();
			if (key.endsWith(textAndMnemonicSuffix))
			{
				String value = resource.getString(key);
				UIManager.put(key, value);
				String text = value.replace("&", "");
				String keyPrefix = key.substring(0,
						key.length() - textAndMnemonicSuffix.length());
				UIManager.put(keyPrefix + "NameText", text);
				UIManager.put(keyPrefix + "Text", text);
				int index = value.indexOf('&');
				if (index >= 0 && index < value.length() - 1)
				{
					UIManager.put(
							key.replace(textAndMnemonicSuffix, "Mnemonic"),
							String.valueOf(Character
									.toUpperCase(value.charAt(index + 1))));
				}
			}
		}
		// Store other properties coming from read resource and give them a
		// higher priority if already set in previous loop
		for (Enumeration<?> it = resource.getKeys(); it.hasMoreElements(); )
		{
			String key = (String) it.nextElement();
			if (!key.endsWith(textAndMnemonicSuffix))
			{
				UIManager.put(key, resource.getString(key));
			}
		}
	}

	/**
	 * Adds focus and mouse listeners to the given <code>textComponent</code>
	 * that will select all its text when it gains focus by transfer.
	 */
	public static void addAutoSelectionOnFocusGain(
			final JTextComponent textComponent)
	{
		class SelectionOnFocusManager extends MouseAdapter
				implements
				FocusListener
		{
			private boolean mousePressedInTextField = false;
			private int selectionStartBeforeFocusLost = -1;
			private int selectionEndBeforeFocusLost = -1;

			@Override
			public void mousePressed(MouseEvent ev)
			{
				this.mousePressedInTextField = true;
				this.selectionStartBeforeFocusLost = -1;
			}

			@Override
			public void focusLost(FocusEvent ev)
			{
				if (ev.getOppositeComponent() == null
						|| SwingUtilities.getWindowAncestor(
						ev.getOppositeComponent()) != SwingUtilities
						.getWindowAncestor(textComponent))
				{
					this.selectionStartBeforeFocusLost = textComponent
							.getSelectionStart();
					this.selectionEndBeforeFocusLost = textComponent
							.getSelectionEnd();
				} else
				{
					this.selectionStartBeforeFocusLost = -1;
				}
			}

			@Override
			public void focusGained(FocusEvent ev)
			{
				if (this.selectionStartBeforeFocusLost != -1)
				{
					EventQueue.invokeLater(() -> {
						// Reselect the same characters in text field
						textComponent.setSelectionStart(
								selectionStartBeforeFocusLost);
						textComponent.setSelectionEnd(
								selectionEndBeforeFocusLost);
					});
				} else if (!this.mousePressedInTextField
						&& ev.getOppositeComponent() != null
						&& SwingUtilities.getWindowAncestor(
						ev.getOppositeComponent()) == SwingUtilities
						.getWindowAncestor(textComponent))
				{
					// Select all characters when text field got the
					// focus because of a transfer
					EventQueue.invokeLater(textComponent::selectAll);
				}
				this.mousePressedInTextField = false;
			}
		}

		SelectionOnFocusManager selectionOnFocusManager = new SelectionOnFocusManager();
		textComponent.addFocusListener(selectionOnFocusManager);
		textComponent.addMouseListener(selectionOnFocusManager);
	}

	public static int showConfirmDialog(JComponent parentComponent,
			JComponent messageComponent, String title,
			final JComponent focusedComponent)
	{
		JOptionPane optionPane = new JOptionPane(messageComponent,
				JOptionPane.PLAIN_MESSAGE, JOptionPane.OK_CANCEL_OPTION);
		parentComponent = SwingUtilities.getRootPane(parentComponent);
		final JDialog dialog = optionPane.createDialog(parentComponent, title);
		dialog.applyComponentOrientation(parentComponent != null
				? parentComponent.getComponentOrientation()
				: ComponentOrientation.getOrientation(Locale.getDefault()));
		if (focusedComponent != null)
		{
			dialog.addComponentListener(new ComponentAdapter()
			{
				@Override
				public void componentShown(ComponentEvent ev)
				{
					requestFocusInWindow(focusedComponent);
					dialog.removeComponentListener(this);
				}
			});
		}
		dialog.setVisible(true);

		dialog.dispose();
		Object value = optionPane.getValue();
		if (value instanceof Integer)
		{
			return (Integer) value;
		} else
		{
			return JOptionPane.CLOSED_OPTION;
		}
	}

	public static void requestFocusInWindow(final JComponent focusedComponent)
	{
		if (!focusedComponent.requestFocusInWindow())
		{
			new Timer(50, ev -> {
				focusedComponent.requestFocusInWindow();
				((Timer) ev.getSource()).stop();
			}).start();
		}
	}

	public static void showMessageDialog(JComponent parentComponent,
			JComponent messageComponent, String title, int messageType,
			final JComponent focusedComponent)
	{
		JOptionPane optionPane = new JOptionPane(messageComponent, messageType,
				JOptionPane.DEFAULT_OPTION);
		parentComponent = SwingUtilities.getRootPane(parentComponent);
		final JDialog dialog = optionPane.createDialog(parentComponent, title);
		dialog.applyComponentOrientation(parentComponent != null
				? parentComponent.getComponentOrientation()
				: ComponentOrientation.getOrientation(Locale.getDefault()));
		if (focusedComponent != null)
		{
			dialog.addComponentListener(new ComponentAdapter()
			{
				@Override
				public void componentShown(ComponentEvent ev)
				{
					requestFocusInWindow(focusedComponent);
					dialog.removeComponentListener(this);
				}
			});
		}
		dialog.setVisible(true);
		dialog.dispose();
	}

	/**
	 * Returns the border of a component where a user may drop objects.
	 */
	public static Border getDropableComponentBorder()
	{
		Border border = null;
		if (OSTools.isMacOSXLeopardOrSuperior())
		{
			border = UIManager.getBorder("InsetBorder.aquaVariant");
		}
		if (border == null)
		{
			border = BorderFactory.createLoweredBevelBorder();
		}
		return border;
	}

	/**
	 * Returns a scroll bar adjustment listener bound to the given
	 * <code>scrollPane</code> view that updates view tool tip when its vertical
	 * scroll bar is adjusted.
	 */
	public static AdjustmentListener createAdjustmentListenerUpdatingScrollPaneViewToolTip(
			final JScrollPane scrollPane)
	{
		return ev -> {
			Point screenLocation = MouseInfo.getPointerInfo().getLocation();
			Point point = new Point(screenLocation);
			Component view = scrollPane.getViewport().getView();
			SwingUtilities.convertPointFromScreen(point, view);
			if (scrollPane.isShowing() && scrollPane.getViewport()
					.getViewRect().contains(point))
			{
				MouseEvent mouseEvent = new MouseEvent(view,
						MouseEvent.MOUSE_MOVED, System.currentTimeMillis(),
						0, point.x, point.y, 0, false, MouseEvent.NOBUTTON);
				if (isToolTipShowing())
				{
					ToolTipManager.sharedInstance().mouseMoved(mouseEvent);
				}
			}
		};
	}

	public static boolean isActive(JComponent c)
	{
		if (c == null)
			return false;
		boolean active = true;
		if (c instanceof JInternalFrame)
			active = ((JInternalFrame) c).isSelected();
		if (active)
		{
			Container parent = c.getParent();
			while (parent != null)
			{
				if (parent instanceof JInternalFrame)
				{
					active = ((JInternalFrame) parent).isSelected();
					break;
				}
				parent = parent.getParent();
			}
		}
		if (active)
			active = WindowTools.isWindowActive(c);
		return active;
	}

	/**
	 * Returns <code>true</code> if a tool tip is showing.
	 */
	public static boolean isToolTipShowing()
	{
		for (Frame frame : Frame.getFrames())
		{
			if (isToolTipShowing(frame))
				return true;
		}
		return false;
	}

	private static boolean isToolTipShowing(Container container)
	{
		if (container instanceof Window)
		{
			for (Window window : ((Window) container).getOwnedWindows())
				if (isToolTipShowing(window))
					return true;
		}
		for (int i = 0; i < container.getComponentCount(); i++)
		{
			Component child = container.getComponent(i);
			if (child instanceof JToolTip && child.isShowing()
					|| child instanceof Container
					&& isToolTipShowing((Container) child))
			{
				return true;
			}
		}
		return false;
	}

	/**
	 * Returns the children of a component of the given class.
	 */
	public static <T extends Component> List<T> findChildren(JComponent parent,
			Class<T> childrenClass)
	{
		List<T> children = new ArrayList<T>();
		findChildren(parent, childrenClass, children);
		return children;
	}

	private static <T extends Component> void findChildren(JComponent parent,
			Class<T> childrenClass, List<T> children)
	{
		for (int i = 0; i < parent.getComponentCount(); i++)
		{
			Component child = parent.getComponent(i);
			if (childrenClass.isInstance(child))
			{
				children.add((T) child);
			} else if (child instanceof JComponent)
			{
				findChildren((JComponent) child, childrenClass, children);
			}
		}
	}

	/**
	 * Returns <code>true</code> if the given rectangle is fully visible at
	 * screen.
	 */
	public static boolean isRectangleVisibleAtScreen(Rectangle rectangle)
	{
		Area devicesArea = new Area();
		GraphicsEnvironment environment = GraphicsEnvironment
				.getLocalGraphicsEnvironment();
		for (GraphicsDevice device : environment.getScreenDevices())
		{
			devicesArea.add(
					new Area(device.getDefaultConfiguration().getBounds()));
		}
		return devicesArea.contains(rectangle);
	}

	/**
	 * Returns a new custom cursor.
	 */
	public static Cursor createCustomCursor(URL smallCursorImageUrl,
			URL largeCursorImageUrl, float xCursorHotSpot, float yCursorHotSpot,
			String cursorName, Cursor defaultCursor)
	{
		if (GraphicsEnvironment.isHeadless())
			return defaultCursor;

		Dimension cursorSize = Toolkit.getDefaultToolkit().getBestCursorSize(16,
				16);

		// If returned cursor size is 0, system doesn't support custom cursor
		if (cursorSize.width == 0)
			return defaultCursor;

		URL cursorImageResource = cursorSize.width > 16
				? largeCursorImageUrl
				: smallCursorImageUrl;
		try
		{
			BufferedImage cursorImage = ImageIO.read(cursorImageResource);
			Point point = new Point(
					Math.min(cursorSize.width - 1,
							Math.round(cursorSize.width * xCursorHotSpot)),
					Math.min(cursorSize.height - 1,
							Math.round(cursorSize.height * yCursorHotSpot)));
			return Toolkit.getDefaultToolkit().createCustomCursor(cursorImage,
					point, cursorName);
		} catch (IOException ex)
		{
			throw new IllegalArgumentException(
					"Unknown resource " + cursorImageResource);
		}
	}

	static void updateComponentFontSize(float scale)
	{
		if (scale == 1)
			return;
		Font buttonFont = updateUIFontSize("Button.font", scale);
		updateUIFontSize("ToggleButton.font", scale);
		updateUIFontSize("RadioButton.font", scale);
		updateUIFontSize("CheckBox.font", scale);
		updateUIFontSize("ColorChooser.font", scale);
		updateUIFontSize("ComboBox.font", scale);
		updateUIFontSize("InternalFrame.titleFont", scale);
		Font labelFont = updateUIFontSize("Label.font", scale);
		updateUIFontSize("List.font", scale);
		updateUIFontSize("MenuBar.font", scale);
		updateUIFontSize("MenuItem.font", scale);
		updateUIFontSize("MenuItem.acceleratorFont", scale);
		updateUIFontSize("RadioButtonMenuItem.font", scale);
		updateUIFontSize("RadioButtonMenuItem.acceleratorFont", scale);
		updateUIFontSize("CheckBoxMenuItem.font", scale);
		updateUIFontSize("CheckBoxMenuItem.acceleratorFont", scale);
		updateUIFontSize("Menu.font", scale);
		updateUIFontSize("Menu.acceleratorFont", scale);
		updateUIFontSize("PopupMenu.font", scale);
		updateUIFontSize("OptionPane.font", scale);
		updateUIFontSize("Panel.font", scale);
		updateUIFontSize("ProgressBar.font", scale);
		updateUIFontSize("ScrollPane.font", scale);
		updateUIFontSize("Viewport.font", scale);
		updateUIFontSize("Slider.font", scale);
		updateUIFontSize("Spinner.font", scale);
		updateUIFontSize("Table.font", scale);
		updateUIFontSize("TabbedPane.font", scale);
		updateUIFontSize("TableHeader.font", scale);
		updateUIFontSize("TextField.font", scale);
		updateUIFontSize("FormattedTextField.font", scale);
		updateUIFontSize("PasswordField.font", scale);
		updateUIFontSize("TextArea.font", scale);
		updateUIFontSize("EditorPane.font", scale);
		updateUIFontSize("TitledBorder.font", scale);
		updateUIFontSize("ToolBar.font", scale);
		updateUIFontSize("ToolTip.font", scale);
		updateUIFontSize("Tree.font", scale);
		UIManager.put("OptionPane.messageFont", labelFont);
		UIManager.put("OptionPane.buttonFont", buttonFont);
	}

	private static Font updateUIFontSize(String fontKey, float resolutionScale)
	{
		Font font = UIManager.getFont(fontKey);
		if (font != null)
		{
			font = font.deriveFont(font.getSize() * resolutionScale);
			UIManager.put(fontKey, font);
		}
		return font;
	}
	public static String getClippedText(String text, FontMetrics fm,
			int maxWidth)
	{
		if ((text == null) || (text.isEmpty()))
			return "";

		int width = fm.stringWidth(text);
		if (width > maxWidth)
		{
			int totalWidth = fm.stringWidth(ELLIPSIS);
			for (int i = 0; i < text.length(); i++)
			{
				totalWidth += fm.charWidth(text.charAt(i));
				if (totalWidth > maxWidth)
					return text.substring(0, i) + ELLIPSIS;
			}
		}
		return text;
	}
	public static String getHtmlText(String simpleHtmlText)
	{
		if (simpleHtmlText == null)
			return null;
		String text = simpleHtmlText.replaceAll("\n", "<br/>");
		text = text.replaceAll(" ", enSpaceStr);
		return "<html>" + text + "</html>";
	}
	public static String getChWrapHtmlText(String longText,
			FontMetrics fontMetrics, int width, boolean spaceable)
	{
		if (longText == null || fontMetrics == null || width < 20)
			return longText;
		String text = longText.replaceAll(" ", "");
		StringBuilder builder = new StringBuilder("<html>");
		char[] chars = text.toCharArray();
		int start = 0, len = 1;
		boolean isEnd = false;
		if (spaceable)
		{
			int spaceW = fontMetrics.stringWidth("我");
			int w = width - spaceW * 3;
			builder.append(chSpaceStr).append(chSpaceStr);
			while (fontMetrics.charsWidth(chars, start, len) < w
					&& len != chars.length)
			{
				len++;
			}
			isEnd = len == chars.length;
			builder.append(chars, start, len);
			builder.append(isEnd ? "</html>" : "<br/>");
			start = len;
			len = 1;
		}
		if (!isEnd)
		{
			while (start + len < chars.length)
			{
				if (fontMetrics.charsWidth(chars, start, len) < width)
				{
					len++;
				} else
				{
					builder.append(chars, start, len).append("<br/>");
					start += len;
					len = 1;
				}
			}
			builder.append(chars, start, len).append("</html>");
		}
		return builder.toString();
	}
	public static ArrayList<String> getChWrapTexts(String longText,
			FontMetrics fontMetrics, int width)
	{
		ArrayList<String> texts = new ArrayList<>();
		if (longText == null || longText.isEmpty())
			return texts;
		if (fontMetrics == null || width < 10)
		{
			texts.add(longText);
			return texts;
		}
		char[] chars = longText.toCharArray();
		int start = 0, len = 1;
		while (start + len < chars.length)
		{
			if (fontMetrics.charsWidth(chars, start, len) < width)
			{
				len++;
			} else
			{
				texts.add(new String(chars, start, len));
				start += len;
				len = 1;
			}
		}
		if (chars.length >= start + len)
			texts.add(new String(chars, start, len));
		return texts;
	}
	public static String getWrapHtmlText(String longText, Font font, int width)
	{
		return getWrapHtmlText(longText, font, width, true);
	}
	public static String getWrapHtmlText(String longText, Font font, int width,
			boolean spaceable)
	{
		if (longText == null || font == null || width < 3)
			return longText;
		return getChWrapHtmlText(longText, GraphicsTools.getFontMetrics(font),
				width, spaceable);
	}

	public static IRichTextLabel[] getIRichTextLabels(String key, String[] chTexts,
			String[] enTexts, Color color)
	{
		IRichTextLabel[] labels = new IRichTextLabel[chTexts.length];
		for (int i = 0; i < labels.length; i++)
		{
			labels[i] = new IRichTextLabel(new ICSetting(key + i, chTexts[i], enTexts[i]));
			labels[i].setForeground(color);
		}
		return labels;
	}

	public static Container arrangeComponents(Component[] components, Container container,
			int startX, int startY, int width, int height, int gap)
	{
		for (int i = 0; i < components.length; i++)
		{
			container.add(components[i]);
			components[i].setBounds(startX, startY + i * (height + gap), width, height);
		}
		return container;
	}
}
